﻿namespace System.Web.Mvc
{
    using System;
    using System.Collections.Specialized;
    using System.Text;
    using System.Web;

    public static class AjaxHelperExtensions
    {
        public static void Initialize(this AjaxHelper ajaxHelper)
        {
            Initialize(ajaxHelper, true);
        }

        public static void Initialize(this AjaxHelper ajaxHelper, bool registerCoreScripts)
        {
            HttpContextBase context = ajaxHelper.ViewContext.HttpContext;

            ScriptData scriptData = (ScriptData)context.Items[typeof(ScriptData)];
            if (scriptData == null)
            {
                //Not yet initialized
                scriptData = new ScriptData();
                context.Items[typeof(ScriptData)] = scriptData;
            }
            scriptData.IncrementCounter();

            if (registerCoreScripts)
            {
                string coreLibrary = "~/Scripts/MicrosoftAjax.debug.js";
                RegisterScript(ajaxHelper, coreLibrary);
            }
        }

        public static void RegisterScript(this AjaxHelper ajaxHelper, string scriptVirtualPath)
        {
            if (String.IsNullOrEmpty(scriptVirtualPath))
            {
                throw new ArgumentNullException("scriptVirtualPath");
            }

            HttpContextBase context = ajaxHelper.ViewContext.HttpContext;
            HttpRequestBase request = context.Request;
            ScriptData scriptData = (ScriptData)context.Items[typeof(ScriptData)];

            if (scriptData == null)
            {
                throw new InvalidOperationException("You must call Ajax.Initialize before calling RegisterScript.");
            }

            string scriptPath = VirtualPathUtility.ToAbsolute(scriptVirtualPath, request.ApplicationPath);
            scriptData.RegisterScript(scriptPath);
        }

        public static void RegisterScriptBehavior(this AjaxHelper ajaxHelper, string id, string type, NameValueCollection options)
        {
            if (String.IsNullOrEmpty(id))
            {
                throw new ArgumentNullException("id");
            }
            if (String.IsNullOrEmpty(type))
            {
                throw new ArgumentNullException("type");
            }

            HttpContextBase context = ajaxHelper.ViewContext.HttpContext;
            ScriptData scriptData = (ScriptData)context.Items[typeof(ScriptData)];

            if (scriptData == null)
            {
                throw new InvalidOperationException("You must call Ajax.Initialize before calling RegisterScriptBehavior.");
            }

            ScriptBehavior behavior = new ScriptBehavior(id, type, options);
            scriptData.RegisterScriptBehavior(behavior);
        }

        /// <summary>
        /// Registers a client script block.
        /// </summary>
        /// <param name="ajaxHelper"></param>
        /// <param name="script">The client script literal to register.</param>
        public static void RegisterScriptBlock(this AjaxHelper ajaxHelper, string script)
        {
            if (String.IsNullOrEmpty(script))
            {
                throw new ArgumentNullException("script");
            }

            HttpContextBase context = ajaxHelper.ViewContext.HttpContext;
            ScriptData scriptData = (ScriptData)context.Items[typeof(ScriptData)];

            if (scriptData == null)
            {
                throw new InvalidOperationException("You must call Ajax.Initialize before calling RegisterScript.");
            }

            scriptData.RegisterScriptBlock(script);
        }

        public static void RenderScripts(this AjaxHelper ajaxHelper)
        {
            HttpContextBase context = ajaxHelper.ViewContext.HttpContext;
            HttpResponseBase response = context.Response;

            // Write out any registered scripts
            ScriptData scriptData = (ScriptData)context.Items[typeof(ScriptData)];
            if (scriptData != null)
            {
                scriptData.DecrementCounter();
                if (scriptData.Counter != 0)
                {
                    return;
                }

                // render script references
                if (HttpContext.Current.IsDebuggingEnabled)
                {
                    foreach (string scriptPath in scriptData.ScriptReferences)
                    {
                        response.Write("<script type=\"text/javascript\" src=\"");
                        response.Write(scriptPath);
                        response.Write("\"></script>");
                    }
                }
                else
                {
                    string[] scriptRefs = new string[scriptData.ScriptReferences.Count];
                    scriptData.ScriptReferences.CopyTo(scriptRefs, 0);
                    response.Write("<script type=\"text/javascript\" src=\"");
                    response.Write("JsCombine.ashx?js=");
                    response.Write(HttpUtility.UrlEncode(string.Join(",", scriptRefs)));
                    response.Write("\"></script>");
                }

                // render script blocks
                if (scriptData.ScriptBehaviors.Count > 0 || scriptData.ScriptBlocks.Count > 0)
                {
                    response.Write("<script type=\"text/javascript\">");
                    response.Write(Environment.NewLine);

                    foreach (ScriptBehavior behavior in scriptData.ScriptBehaviors)
                    {
                        response.Write("Sys.Application.add_init(function() { ");
                        response.Write(CreateComponentScript(behavior.ID, behavior.Type, behavior.Options));
                        response.Write(" });");
                        response.Write(Environment.NewLine);
                    }

                    foreach (string scriptBlock in scriptData.ScriptBlocks)
                    {
                        response.Write(scriptBlock);
                        response.Write(Environment.NewLine);
                    }

                    response.Write("</script>");
                }
            }
        }

        private static string CreateComponentScript(string id, string type, NameValueCollection properties)
        {
            StringBuilder script = new StringBuilder();
            script.Append("$create(");
            script.Append(type);
            script.Append(", {");

            if (properties != null)
            {
                bool firstProperty = true;
                foreach (string key in properties.Keys)
                {
                    if (firstProperty == false)
                    {
                        script.Append(", ");
                    }

                    script.Append(key);
                    script.Append(":'");
                    script.Append(properties[key]);
                    script.Append("'");

                    firstProperty = false;
                }
            }

            script.Append("}, null, "); // events
            script.Append("null, "); // references

            script.Append("$get('");
            script.Append(id);
            script.Append("'));");
            return script.ToString();
        }
    }
}
